1 /*
2 Copyright: Marcelo S. N. Mancini (Hipreme|MrcSnm), 2018 - 2021
3 License:   [https://creativecommons.org/licenses/by/4.0/|CC BY-4.0 License].
4 Authors: Marcelo S. N. Mancini
5 
6 	Copyright Marcelo S. N. Mancini 2018 - 2021.
7 Distributed under the CC BY-4.0 License.
8    (See accompanying file LICENSE.txt or copy at
9 	https://creativecommons.org/licenses/by/4.0/
10 */
11 module hip.systems.hotload;
12 
13 version(Load_DScript):
14 import hip.filesystem.hipfs;
15 import hip.util.system;
16 import hip.util.path;
17 import hip.console.log;
18 import hip.error.handler;
19 
20 final class HotloadableDLL
21 {
22     void* lib;
23 
24     immutable string trueLibPath;
25     void delegate (void* libPointer) onDllLoad;
26     string tempDll;
27     string tempPdb;
28     this(string path, void delegate (void* libPointer) onDllLoad)
29     {
30         ErrorHandler.assertExit(path != null, "DLL path should not be null:
31 Call `dub -c script -- path/to/project` dub -c script requires that argument.");
32 
33         import hip.util.path;
34         import hip.util.file;
35 
36         path = absolutePath(path, getcwd).normalizePath;
37         if(HipFS.absoluteExists(path) && HipFS.absoluteIsDir(path))
38             path = joinPath(path, path.filenameNoExt);
39         if(!dynamicLibraryIsLibNameValid(path))
40             path = dynamicLibraryGetLibName(path);
41         trueLibPath = path;
42         this.onDllLoad = onDllLoad;
43         load(path);
44     }
45     protected bool load(string path)
46     {
47         if(!HipFS.absoluteExists(path))
48         {
49             import hip.console.log;
50             logln("Path '", path, "' does not exists while trying to find for a HotloadableDLL");
51             return false;
52         }
53         //Create dll_hiptempdll
54         tempDll = getTempName(path);
55 
56         copy(path, tempDll);
57 
58         // version(Windows)
59         // {{
60         //     import std.process:executeShell;
61         //     import hip.util.path;
62         //     string tempDirectory = joinPath(path.dirName, "temp");
63         //     mkdirRecurse(tempDirectory);
64             
65         //     string thePdb = path.extension("pdb");
66         //     tempPdb = getTempName(thePdb);
67         //     if(exists(thePdb))
68         //     {
69         //         copy(thePdb, joinPath(tempDirectory, thePdb.baseName));
70         //         rename(thePdb, tempPdb);
71         //     }
72 
73         // }}
74 
75 
76         loglnInfo("Loading dll ", path);
77         lib = dynamicLibraryLoad(tempDll);
78         if(onDllLoad && lib != null)
79             onDllLoad(lib);
80         else if(lib == null)
81             loglnError("Could not load dll ", path);
82         return lib != null;
83     }
84     /** 
85      * 
86      * Params:
87      *   path = File path
88      * Returns: filePath.(ext)_hiptempdll
89      */
90     static string getTempName(string path)
91     {
92         import hip.util.string;
93         string d = dirName(path);
94         string n = baseName(path);
95         string ext = extension(n);
96         
97         int ind = lastIndexOf(n, "_hiptemp");
98         if(ind != -1)
99             return path;
100         return joinPath(d, n[0..$-(ext.length+1)]~"_hiptemp."~ext);
101     }
102 
103     void reload()
104     {
105         if(lib != null)
106         {
107             dynamicLibraryRelease(lib);
108             lib = null;
109         }
110         load(trueLibPath);
111     }
112 
113     void dispose()
114     {
115         import hip.error.handler;
116         if(lib != null)
117         {
118             if(!dynamicLibraryRelease(lib))
119                 return ErrorHandler.showErrorMessage("Could not unload the dll ", tempDll);
120             
121             foreach(remFile; [tempDll, tempPdb]) 
122             {
123                 try
124                 {
125                     if(remFile && !HipFS.absoluteRemove(remFile))
126                     {
127                         ErrorHandler.showErrorMessage("Could not remove ", remFile);
128                     }
129                 }
130                 catch(Exception e)
131                 {
132                     ErrorHandler.showErrorMessage("Could not remove file ", remFile);
133                 }
134             }
135         }
136     }
137 }
138 
139 private void copy(string input, string output)
140 {
141     import hip.filesystem.hipfs;
142     void[] data;
143     if(!HipFS.absoluteRead(input, data))
144         throw new Error("Could not read input file "~input);
145 
146     try
147     {
148         if(!HipFS.absoluteWrite(output, data))
149             throw new Error("Could not copy from "~input~" to "~output);
150     }
151     catch (Exception e) {
152 
153     }
154 
155 }